﻿using System.Collections.Generic;
using System.Xml;
using System.Text.RegularExpressions;

namespace OrganizationName.Xml.Extensions
{
    public class XmlExtendedDocumentFilterExtension : IXmlExtendedDocumentExtension
    {
        protected List<string> definedSwitches = new List<string>();

        public XmlExtendedDocumentFilterExtension()
        {
        }

        public XmlExtendedDocumentFilterExtension(string switches)
            : this()
        {
            if (switches != null)
            {
                definedSwitches.AddRange(switches.Split(','));
            }
        }

        void IXmlExtendedDocumentExtension.Extend(System.Xml.XmlDocument xmlDocument)
        {
            XmlNodeList elementList = xmlDocument.SelectNodes("//*[@__filter]");

            foreach (XmlElement element in elementList)
            {
                if (Test(element))
                {
                    element.Attributes.Remove(element.Attributes["__filter"]);
                }
                else
                {
                    element.ParentNode.RemoveChild(element);
                }
            }
        }

        protected virtual bool Test(XmlElement element)
        {
            string originalFilter = element.Attributes["__filter"].Value;

            if (!string.IsNullOrEmpty(originalFilter))
            {
                string modifiedFilter = Regex.Replace(originalFilter, @"\[([A-Za-z0-9_]+)]", SwitchEvaluator);

                modifiedFilter = modifiedFilter.Replace(" ", "");

                while (modifiedFilter.Length > 1)
                {
                    string currentFilter = modifiedFilter;

                    modifiedFilter = modifiedFilter
                        .Replace("!1", "0")
                        .Replace("!0", "1")
                        .Replace("1and1", "1")
                        .Replace("1and0", "0")
                        .Replace("0and1", "0")
                        .Replace("0and0", "0")
                        .Replace("1or1", "1")
                        .Replace("1or0", "1")
                        .Replace("0or1", "1")
                        .Replace("0or0", "0")
                        .Replace("(1)", "1")
                        .Replace("(0)", "0");

                    if (currentFilter == modifiedFilter)
                    {
                        throw new XmlExtendedDocumentException("The xml filter value \"" + originalFilter + "\" cannot be evaluated to a boolean value. The name of the element containing the invalid filter is " + element.Name);
                    }
                }
                return modifiedFilter == "1";
            }
            else
            {
                return true;
            }
        }

        protected virtual string SwitchEvaluator(Match target)
        {
            if (definedSwitches != null)
            {
                if (definedSwitches.Contains(target.Groups[1].Value))
                {
                    return "1";
                }
            }
            return "0";
        }
    }
}
